import type { ChromeReleaseChannel } from 'puppeteer';
import { createChildLogger } from '../../utils/logger.js';
import { LocalMermaidManager } from './mermaid-local.js';

// 持久化的 Mermaid 渲染器：复用单个浏览器实例/页面，降低频繁启动/关闭带来的不稳定
export class PersistentMermaidRenderer {
  private log = createChildLogger('convert/mermaid-persistent');
  private static _instance: PersistentMermaidRenderer | null = null;
  static get instance(): PersistentMermaidRenderer {
    if (!this._instance) this._instance = new PersistentMermaidRenderer();
    return this._instance;
  }

  private browser: any | null = null;
  private page: any | null = null;
  private starting: Promise<void> | null = null;
  private renderCount = 0;

  constructor() {
    // 进程退出时优雅清理
    const stop = this.stop.bind(this);
    process.once('exit', stop);
    process.once('SIGINT', () => {
      stop().finally(() => process.exit(0));
    });
    process.once('SIGTERM', () => {
      stop().finally(() => process.exit(0));
    });
  }

  async start(): Promise<void> {
    if (this.browser && this.page) return;
    if (this.starting) return this.starting;

    this.starting = (async () => {
      const channel = process.env.BROWSER_CHANNEL?.trim();
      const executablePath = process.env.BROWSER_EXECUTABLE?.trim();

      const puppeteer = (await import('puppeteer')).default;
      const channelEnv = process.env.BROWSER_CHANNEL?.trim();
      let validChannel: ChromeReleaseChannel | undefined;
      if (
        channelEnv === 'chrome' ||
        channelEnv === 'chrome-beta' ||
        channelEnv === 'chrome-canary' ||
        channelEnv === 'chrome-dev'
      ) {
        validChannel = channelEnv as ChromeReleaseChannel;
      }
      this.browser = await puppeteer.launch({
        headless: true,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-gpu',
          '--no-zygote',
          '--disable-software-rasterizer',
          '--disable-background-timer-throttling',
          '--disable-backgrounding-occluded-windows',
          '--disable-renderer-backgrounding',
          '--no-default-browser-check',
          '--no-first-run',
          '--disable-default-apps',
          '--disable-extensions',
          '--disable-translate',
          '--metrics-recording-only',
          '--mute-audio',
        ],
        protocolTimeout: 120_000,
        ...(validChannel ? { channel: validChannel } : {}),
        ...(executablePath ? { executablePath } : {}),
      });
      const version = await this.browser.version();
      this.log.info(
        `🚀 [Mermaid-Persist][Puppeteer] 启动 -> ${version}; channel=${
          channel ?? 'default'
        }; exec=${executablePath ?? 'default'}`
      );
      this.page = await this.browser.newPage();
      try {
        await this.page.emulateMediaType('screen');
      } catch {}

      // 预加载基础页面与 mermaid 脚本
      const mermaidScript = await LocalMermaidManager.getMermaidScript();
      const chineseFont = process.env.MERMAID_FONT || 'Microsoft YaHei';
      const theme = process.env.MERMAID_THEME || 'default';

      const baseHtml = `<!DOCTYPE html>
<html><head><meta charset="utf-8" />
<style>
  body{margin:0;padding:20px;font-family:${chineseFont},sans-serif;background:white}
  #container{display:block}
  .mermaid{text-align:center}
</style>
<script>${mermaidScript}</script>
<script>
  window.__initMermaid = function(theme, font){
    try{
      if (typeof mermaid !== 'undefined'){
        mermaid.initialize({startOnLoad:false, theme: theme||'${theme}', fontFamily: font||'${chineseFont}', fontSize:14, securityLevel:'loose', logLevel:'error'});
      }
    }catch(e){ console.error('init mermaid failed', e); }
  };
</script>
</head>
<body>
  <div id="container"><div class="mermaid"></div></div>
</body></html>`;

      // 使用 setContent + 内联脚本，避免 data:URL 长度限制
      await (this.page as any).setContent(baseHtml, {
        waitUntil: 'domcontentloaded',
        timeout: 60_000,
      });
      await (this.page as any).evaluate(
        (t: string, f: string) => {
          (window as any).__initMermaid?.(t, f);
        },
        theme,
        chineseFont
      );
    })();

    try {
      await this.starting;
    } finally {
      this.starting = null;
    }
  }

  async renderToImage(
    mermaidCode: string,
    outputPath: string,
    options: { width?: number; height?: number; chineseFont?: string; theme?: string } = {}
  ): Promise<string> {
    try {
      await this.start();
      this.renderCount++;
      const theme = options.theme || process.env.MERMAID_THEME || 'default';
      const font = options.chineseFont || process.env.MERMAID_FONT || 'Microsoft YaHei';
      const width = options.width || 600;
      const height = options.height || 400;

      // 设定视口
      try {
        await (this.page as any).setViewportSize?.({ width, height });
      } catch {}
      try {
        await (this.page as any).setViewport?.({ width, height });
      } catch {}

      // 在页面中渲染 mermaid
      const ok: boolean = await (this.page as any).evaluate(
        (code: string, theme: string, font: string) => {
          return new Promise<boolean>(resolve => {
            try {
              const el = document.querySelector('.mermaid');
              if (!el) return resolve(false);
              (window as any).mermaid?.initialize({
                startOnLoad: false,
                theme,
                fontFamily: font,
                fontSize: 14,
                securityLevel: 'loose',
                logLevel: 'error',
              });
              (window as any).mermaid
                ?.render('graph' + Math.random().toString(36).slice(2), code)
                .then((res: any) => {
                  el.innerHTML = res.svg;
                  resolve(true);
                })
                .catch((e: any) => {
                  /* page console suppressed */ el.innerHTML = '<div>📊 Mermaid图表渲染失败</div>';
                  resolve(false);
                });
            } catch (e) {
              /* page console suppressed */
              resolve(false);
            }
          });
        },
        mermaidCode,
        theme,
        font
      );

      // 等待渲染完成/占位
      try {
        await (this.page as any).waitForFunction(
          () => {
            const el = document.querySelector('.mermaid');
            return el && (el.innerHTML.includes('svg') || el.innerHTML.includes('渲染失败'));
          },
          { timeout: 15_000 }
        );
      } catch {}

      // 截图
      const el = await (this.page as any).$('.mermaid');
      if (!el) throw new Error('未找到Mermaid元素');
      await el.screenshot({ path: outputPath });
      return outputPath;
    } catch (e) {
      // 尝试自愈：重建页面
      this.log.warn(
        '[Mermaid-Persist] 渲染失败，尝试重建页面一次:',
        e instanceof Error ? e.message : String(e)
      );
      try {
        await this.recreatePage();
        const retry = await this.renderToImage(mermaidCode, outputPath, options);
        return retry;
      } catch (e2) {
        return `error:${e2 instanceof Error ? e2.message : String(e2)}`;
      }
    }
  }

  private async recreatePage(): Promise<void> {
    if (!this.browser) return this.start();
    try {
      if (this.page && !this.page.isClosed?.()) await this.page.close();
    } catch {}
    this.page = await (this.browser as any).newPage();
    const mermaidScript = await LocalMermaidManager.getMermaidScript();
    const chineseFont = process.env.MERMAID_FONT || 'Microsoft YaHei';
    const theme = process.env.MERMAID_THEME || 'default';
    const baseHtml = `<!DOCTYPE html><html><head><meta charset="utf-8" />
<style>body{margin:0;padding:20px;font-family:${chineseFont},sans-serif;background:white}.mermaid{text-align:center}</style>
<script>${mermaidScript}</script>
<script>window.__initMermaid=function(theme,font){try{if(typeof mermaid!=='undefined'){mermaid.initialize({startOnLoad:false, theme:theme||'${theme}', fontFamily:font||'${chineseFont}', fontSize:14, securityLevel:'loose', logLevel:'error'});}}catch(e){}}</script>
</head><body><div class="mermaid"></div></body></html>`;
    // 使用 setContent + 内联脚本，避免 data:URL 长度限制
    await (this.page as any).setContent(baseHtml, {
      waitUntil: 'domcontentloaded',
      timeout: 60_000,
    });
    await (this.page as any).evaluate(
      (t: string, f: string) => {
        (window as any).__initMermaid?.(t, f);
      },
      theme,
      chineseFont
    );
  }

  async stop(): Promise<void> {
    try {
      if (this.page && !this.page.isClosed?.()) await this.page.close();
    } catch {}
    try {
      if (this.browser) await this.browser.close();
    } catch {}
    this.page = null;
    this.browser = null;
    this.log.info('[Mermaid-Persist] 已停止');
  }
}
